#include <stack>
#include <string>
using namespace std;

//----------------------------------------------------------------------------
// Exception handling.

class BasicException {
	public:
		BasicException(string description) { m_description = description; }
		string GetMessage(void) { return m_description; }
	protected:
		string m_description;
};

class InternalException : public BasicException {1
	public:
		InternalException(string method, string description) : BasicException(method + "\t" + description) {}
};


//----------------------------------------------------------------------------
// The game.

enum Piece { EMPTY , BLACK, WHITE };

class Board {
	public:
		Board(int size) throw(InternalException) {
			m_size = size;

			m_board = new Piece*[m_size];
			for (int i = 0; i < m_size; i++) {
				m_board[i] = new Piece[m_size];
		};
		~Board(void) throw() {
			for (int i = 0; i < m_size; i++)
				delete [] m_board[i];
			delete [] m_board;
		}
		Board & Board(const Board & other) {
			if (m_size != other.GetSize()) {
				InternalException e(
					"Board::Board() (copy constructor)",
					"The dimensions of the passed in board did not match."
				);
				throw(e);
			}

			for (int i = 0; i < m_size; i++)
				for (int j = 0; j < m_size;)
					board[i][j] = boardSituation[i][j];
		}
		Piece GetPiece(int x, int y) { return m_board[x][y]; }
		void SetPiece(Piece piece, int x, int y) { board[x][y] = piece; }
		int GetSize(void) const throw() { return m_size; }
	private:
		int m_size;
		Piece ** m_board;
};

enum Directions { LEFT, LEFTUP, UP, RIGHTUP, RIGHT, RIGHTDOWN, DOWN, LEFTDOWN };

class Game {
	public:
		Game(string player1, string player2) {
			m_active = BLACK; // Black always begins.
			moveNumber = 0;

			m_player1 = player1;
			m_player2 = player2;

			// Initialize board.
			m_board = new Board(SIZE);
			SetStartPosition(m_board);
		}
		Game(string player1, string player2, int moveNumber, stack<Board *> history);
		~Game(void) {
			m_player1->plays++;
			m_player2->plays++;

			delete m_history;
			delete m_board;
		}
		int GetBoardSize(void) const { return SIZE; }
		Piece GetActive const { return m_active; }
		bool ValidMovePossible(Piece piece) const {
			bool moveFound = false;

			for (int x = 0; !movefound && x < SIZE; x++)
				for (int y = 0; !movefound && y < SIZE; y++)
					moveFound = IsValidMove(piece, x, y);
			return moveFound;
		}
		void ToggleActive(void) throw() {
			if (m_active == BLACK)
				m_active = WHITE;
			else
				m_active = BLACK;
		}
		bool TryPieceMove(Piece piece, int x, int y) {
			// Controleer of het stuk van het juiste type is.
			if (piece != m_active) {
				// TODO: throw internal exception
			}

			if (IsValidMove(piece, x, y)) {
				m_board->SetPiece(piece, x, y);
				CheckAllDirections(piece, x, y, true);
				ToggleActive();
				return true;
			}
			else
				return false;
		}
	private:
		Piece m_active;
		int m_moveNumber;
		stack<Board *> m_history;
		Board * m_board;
		string m_player1, m_player2;
		static const int SIZE;
		void SetStartPosition(Board * board) {
			board->SetPiece(BLACK, 4, 5);
			board->SetPiece(WHITE, 4, 4);
			board->SetPiece(BLACK, 5, 4);
			board->SetPiece(WHITE, 5, 5);
			m_moveNumber += 4;
		}
		void FlipPieces(int x1, int y1, int x2, int y2) {
			
		}
		bool IsValidMove(Piece piece, int x, int y) const {
			return (m_board[x][y] == EMPTY) && CheckAllDirections(piece, x, y, false);
		}
		bool IsWithinBounds(int x, int y) { return ((x >= 0 && x < SIZE) && (y >= 0 && y < SIZE)); }
		bool CheckAllDirections(Piece piece, int x, int y, bool performMove) {
			// TODO: ga alle 8 richtingen af
			// - het eerste of meer elementen moeten vd andere kleur zijn
			// - het laatste element dat niet leeg is moet van dezelfde kleur zijn
			// Als hier aan voldaan is, moeten alle elementen er tussen in geflipt worden (FlipPieces()) INDIEN performMove true is

			// Van links naar linksboven naar ... naar linksonder.
			for (int direction = LEFT; i <= DOWNLEFT; i++) {
				switch (direction) {
					case LEFT:
						break;
					case LEFTUP:
						break;
					case UP:
						break;
					case RIGHTUP:
						break;
					case RIGHT:
						break;
					case RIGHTDOWN:
						break;
					case DOWN:
						break;
					case DOWNLEFT:
						break;
				}
			}
		}

};

/*
// TODO: in de .cpp vlammen
Game::SIZE = 8;
*/

class Player {
	public:
		Player(string name, int m_victories = 0, int m_plays = 0);
		~Player(void);
		int GetVictories(void) const { return m_victories; }
		int GetPlays(void) const { return m_plays; }
		void IncreaseVictories(void) { m_victories++; }
		void IncreasePlays(void) { m_plays++; }
	private:
		string m_name;
		int m_victories;
		int m_plays;
};

#include <map>
using namespace std;

struct playerNameCompare {
  bool operator()(string name1, string name2) const {
	return name1.compare(name2) < 0;
  }
};

class PlayerDB {
	public:
		PlayerDB(void) { ImportFromFile(); };
		~PlayerDB(void) { delete m_db; }
		Player * GetPlayer(string name);
		void NewPlayer(Player * player);
	private:
		map<string, Player *, playerNameCompare> * m_db;
		static const string FILENAME;
		void ImportFromFile(void);
		void ExportToFile(void);
};

/*
// TODO: in de .cpp vlamme
PlayerDB::FILENAME = "player.rdb"; // "rdb" voor Reversi DataBase
*/

//----------------------------------------------------------------------------
// main()

void init(void) {
	/*
		1) PlayerDB inlezen, als die bestaat
		2) check of er een opgeslagen spel is
			- indien ja, openen
			- indien nee, spelernamen vragen
				- indien nieuwe spelers, aan PlayerDB toevoegen
		3) begin spel
	*/
}

void quit(void) {
	/*
		1) check of er een spel bezig is (dus laatste zet nog niet bereikt)
			 - indien ja, sla dit op
	*/
}

int main(int argc, char * argv[]) {
	init();

	Game * game;
	try {
		game = new Game(string player1, string player2);
		game.Start();
	}
	catch (BasicException e) {
		cout << e.GetMessage() << endl;
		return 1;
	}
	return 0;
}




/*
Mogelijke extra's:
//- custom spelbordgrootte
//- custom startposities (random?)
1) unlimited undo (en eventueel redo)
2) statistieken per speler
3) automatisch na iedere beurt het gehele spel (dus inclusief alle undo's) saven => je kan later verder spelen en als het spel crasht, ben je niet alles kwijt

*/
